package org.bellusoft.xbeam.framework.launch;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.bellusoft.xbeam.contract.concurrent.NotThreadsafe;
import org.bellusoft.xbeam.framework.FrameworkFactoryImpl;
import org.bellusoft.xbeam.framework.XbeamConstants;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.launch.FrameworkFactory;

@NotThreadsafe
public class Launcher {
	
	private final FrameworkFactory frameworkFactory = new FrameworkFactoryImpl();
	
	private final Map<String,String> configuration ;
	
	private final String workingDirectory ;  
	
	private final static String[] Framework_Config_Property_Keys = {
		
		XbeamConstants.Xbeam_Bundle_Start_Level,
		XbeamConstants.Xbeam_Initial_Bundle_Config,
		XbeamConstants.Xbeam_Initial_Bundle_Dir,
		XbeamConstants.Xbeam_Start_Setting,
		
		Constants.FRAMEWORK_BUNDLE_PARENT, 
		Constants.FRAMEWORK_BEGINNING_STARTLEVEL,
		Constants.FRAMEWORK_STORAGE,
		Constants.FRAMEWORK_STORAGE_CLEAN,
		Constants.FRAMEWORK_SYSTEMPACKAGES,
		Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA,
		Constants.FRAMEWORK_BOOTDELEGATION
	}; 
	
	private Launcher() {
		configuration = new HashMap<String, String>();
		workingDirectory = System.getProperty("user.dir");
	}
	
	private void launch() throws LaunchException {
		
		Framework framework = frameworkFactory.newFramework(configuration);
		
		try {
			framework.init() ;
		} catch (BundleException e) {
			throw new LaunchException("Error when initing Xbeam Framework",e);
		} 
		
		try {
			framework.start();
		} catch (BundleException e) {
			throw new LaunchException("Error when starting Xbeam Framework",e);
		}
		
		try {
			framework.waitForStop(0);
		} catch (InterruptedException e) {
			throw new LaunchException("The main thread of xbeam framework is interrupted",e);
		}
		
	}
	
	public static void main(String[] args) {
		Launcher launcher = new Launcher();
		try{
			launcher.fillPropertiesUsingFrameworkConfig();
			launcher.overidePropertiesUsingSystemProperties();
			launcher.launch();
		}catch (Throwable t) {
			System.err.println("Xbeam launch error: ");
			t.printStackTrace() ; 
			try{
				recordLauncherException(t);
			}catch(Exception e){
				System.err.println("Error when recording launch error to file:");
				e.printStackTrace() ; 
			}
			Runtime.getRuntime().exit(1);
		}
	}

	private static void recordLauncherException(Throwable t) throws IOException{
		File launchErrorFile = new File(System.getProperty("user.dir")+File.pathSeparator+"xbeamlaunch"+System.currentTimeMillis()+".err");
		PrintWriter writer = null ; 
		try{
			new PrintWriter(new OutputStreamWriter(new FileOutputStream(launchErrorFile)));
			t.printStackTrace(writer);
		}finally{
			if(writer!=null){
				writer.close();
			}
		}
	}

	private void overidePropertiesUsingSystemProperties() {
		overidePropertiesIfExist(System.getProperties(), Framework_Config_Property_Keys);
	}

	private void fillPropertiesUsingFrameworkConfig() throws LaunchException {
		String xbeamConfigProperty = System.getProperty(XbeamConstants.Xbeam_Config);
		File f ; 
		if(xbeamConfigProperty!=null && !xbeamConfigProperty.trim().isEmpty()){
			xbeamConfigProperty = xbeamConfigProperty.trim() ;
			f = new File(xbeamConfigProperty);
			//if xbeam.config system property exist, it must be a valid, readable file path, otherwise launch would fail.
			if(!f.exists()||(!f.isFile())){
				throw new LaunchException("Xbeam config file ["+xbeamConfigProperty+"] is not exist or is not a file."); 
			}
		}else{
			xbeamConfigProperty = workingDirectory+File.pathSeparator+XbeamConstants.Xbeam_Config;
			f = new File(xbeamConfigProperty);
			//if xbeam.config default file is not exist, launch will continue ; 
			if(!f.exists()||(!f.isFile())){
				return ; 
			}
		}
		if(!f.canRead()){
			f.setReadable(true);
		}
		if(!f.canRead()){
			throw new LaunchException("Xbeam config file ["+xbeamConfigProperty+"] can not be read.");
		}
		Properties xbeamConfig = new Properties();
		//TODO: delete follow lines, only UTF-8 is supported. 
		String fileEncoding = System.getProperty("file.encoding") ; 
		if(fileEncoding==null){
			fileEncoding = "UTF-8" ; 
		}
		Reader propertyReader = null;
		try{
			propertyReader = new InputStreamReader(new FileInputStream(f),fileEncoding);
			xbeamConfig.load(propertyReader);
		}catch (Exception e) {
			throw new LaunchException("Error when loading xbeam config file ["+xbeamConfigProperty+"]",e);
		}finally{
			if(propertyReader!=null){
				try {
					propertyReader.close();
				} catch (IOException e) {
				}
			}
		}
		overidePropertiesIfExist(xbeamConfig, Framework_Config_Property_Keys);
	}

	private void overidePropertiesIfExist(Properties overideProperties, String[] propertyKeys) {
		for(String key : propertyKeys){
			if(overideProperties.contains(key)){
				configuration.put(key, overideProperties.getProperty(key));
			}
		}
	}


}
